#ifndef MISC_MISC_H
#define MISC_MISC_H


#include "SDL.h"

#include <cmath>

namespace Misc {


// utility function that calls less-than operator for the class
template <class T> inline bool lessThan(const T * a, const T * b) { if (a && b) return ((*a) < (*b)); else return false; }

enum Direction {
	DIRECTION_UP,
	DIRECTION_DOWN,
	DIRECTION_LEFT,
	DIRECTION_RIGHT

};

struct Vector2d {
	double x; 
	double y;
	inline Vector2d(double x = 0, double y = 0) : x(x), y(y) { };
	inline bool operator==(const Vector2d & vector) { return (this->x == vector.x && this->y == vector.y); }
	inline Vector2d & operator=(const Vector2d & s) { x = s.x; y = s.y; return *this; };
	inline Vector2d & operator*=(double scalar) { return (*this = (*this * scalar)); };
	inline Vector2d & operator+=(const Vector2d & op) { return (*this = (*this + op)); };
	inline Vector2d & operator-=(const Vector2d & op) { return (*this = (*this - op)); };
	inline Vector2d operator*(double scalar) { return Vector2d(x * scalar, y * scalar); };
	inline Vector2d operator+(const Vector2d & op) { return Vector2d(x + op.x, y + op.y); };
	inline Vector2d operator-(const Vector2d & op) { return Vector2d(x - op.x, y - op.y); };
	inline double size() { return std::abs(std::sqrt(x*x + y*y)); };
	inline double distance(const Vector2d & op) { double dx = (op.x - x), dy = (op.y - y); return std::abs(std::sqrt(dx*dx + dy*dy)); };
	inline Vector2d & normalize() { double mag = size(); x /= mag; y /= mag; return *this; }; // normalize self
	inline Vector2d normalized() { double mag = size(); return Vector2d(x / mag, y / mag); };
	/*  < a cosα - b senα , b cosα + a senα > */
	inline Vector2d & rotate(double a) { a *= 0.0174532925; x = x*std::cos(a) - y*std::sin(a); y=x*std::sin(a) + y*std::cos(a); return this->normalize(); };
	inline Vector2d rotated(double a) {  a *= 0.0174532925; return (Vector2d(x*std::cos(a) - y*std::sin(a), x*std::sin(a) + y*std::cos(a))).normalize(); };
	
};

/* Frame things */
struct Frame {
	Sint16 x; 
	Sint16 y; 
	Uint16 w; 
	Uint16 h; 
	inline Frame(Sint16 x = 0, Sint16 y = 0, Uint16 w = 0, Uint16 h = 0); 
	inline Frame(const Frame & source); 
	inline  operator SDL_Rect() const; 
	static inline Frame centeredIn(Uint16 screenWidth, Uint16 screenHeight, Uint16 frameWidth, Uint16 frameHeight); // Gives a frame centered to the screen coordinates with given size
	static inline Frame centeredAt(Sint16 posX, Sint16 posY, Uint16 width, Uint16 height); // Creates a frame centered at given position with given size
	inline Frame & operator=(Frame & source); 
	inline Frame & operator=(const Frame & source); 
	inline void centralizeAt(Sint32 x, Sint32 y); 
	inline void centralizeIn(const Frame & relFrame); 
};
inline Frame::Frame(Sint16 x, Sint16 y, Uint16 w, Uint16 h)  : 
	x(x),
	y(y),
	w(w),
	h(h)  {
}

inline Frame::Frame(const Frame & source) : 
	x(source.x),
	y(source.y),
	w(source.w),
	h(source.h)  {
}

inline  Frame::operator SDL_Rect() const {
	SDL_Rect r = { x, y, w, h };
	return r;
}

// Gives a frame centered to the screen coordinates with given size
inline Frame Frame::centeredIn(Uint16 screenWidth, Uint16 screenHeight, Uint16 frameWidth, Uint16 frameHeight)
{
	return Misc::Frame(screenWidth/2 - frameWidth/2, screenHeight/2 - frameHeight/2, frameWidth, frameHeight);
}

// Creates a frame centered at given position with given size
inline Frame Frame::centeredAt(Sint16 posX, Sint16 posY, Uint16 width, Uint16 height)
{
	return Misc::Frame(posX - width/2, posY - height/2, width, height);
}

inline Frame & Frame::operator=(Frame & source){
	x = source.x;
	y = source.y;
	w = source.w;
	h = source.h;
	return *this;
}

inline Frame & Frame::operator=(const Frame & source){
	return (this->operator=(const_cast<Frame &> (source)));
}

inline void Frame::centralizeAt(Sint32 x, Sint32 y){
	this->x = x - w/2;
	this->y = y - h/2;
}

inline void Frame::centralizeIn(const Frame & relFrame){
	this->x = relFrame.w/2 - this->w/2;
	this->y = relFrame.h/2 - this->h/2;
}


} // namespace Misc
#endif
